{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "在本教程中，我们将查看各种Scikit Learn模型的分数，并使用Yellowbrick的可视化诊断工具对它们进行比较，以便为我们的数据选择最佳的模型。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# 1 使用说明\n",
    "## 1.1 模型选择三原则\n",
    "关于机器学习的讨论通常以单一的模型选择为特点。不管是logistic回归、随机森林、贝叶斯方法，还是人工神经网络，机器学习的实践者通常很快就能表达他们的偏好。原因主要是历史原因。尽管现代的第三方机器学习库使得多个模型的部署变得几乎微不足道，但传统上，即使是其中一个算法的应用和调整都需要多年的研究。因此，机器学习的实践者往往对特定的（可能更熟悉的）模型有强烈的偏好。\n",
    "然而，模型选择要比简单地选择“正确”或“错误”算法更为微妙。实际上，工作流程包括：\n",
    "+ 选择和/或工程最小和最具预测性的特征集\n",
    "+ 从模型族中选择一组算法\n",
    "+ 调整算法超参数以优化性能\n",
    "\n",
    "最近，通过网格搜索方法、标准化的api和基于GUI的应用程序，这个工作流的大部分已经实现了自动化。然而，在实践中，人类的直觉和指导比穷尽搜索更有效地磨练质量模型。通过可视化模型选择过程，数据科学家可以转向最终的、可解释的模型，并避免陷阱。\n",
    "\n",
    "Yellowbrick库是一种用于机器学习的诊断可视化平台，它使数据科学家可以控制模型的选择过程。Yellowbrick通过新的核心对象：Visualizer扩展了Scikit-Learn API。可视化工具允许在Scikit-Learn流水线过程中对可视模型进行拟合和转换，从而在整个高维数据转换过程中提供可视化诊断。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 1.2 关于数据\n",
    "本教程使用Yellowbrick 示例数据集模块中的蘑菇数据。我们的目标是根据蘑菇的特征来预测蘑菇是否有毒或可食用。蘑菇数据的YB版本不同于[UCI机器学习存储库](http://archive.ics.uci.edu/ml/)中的蘑菇数据集。Yellowbrick版本已被有意修改，使建模更具挑战性。这些数据包括对蘑菇和麻风菌科23种有鳃蘑菇的假设样本的描述。每一个物种都被确定为绝对可食用，绝对有毒，或未知的食用性和不推荐（后一类是与有毒的一类相结合）。\n",
    "\n",
    "Yellowbrick的数据包含3个属性的信息和8124个蘑菇实例的目标值（4208个可食，3916个有毒）。\n",
    "\n",
    "让我们加载数据：\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "    shape surface   color\n",
      "0  convex  smooth  yellow\n",
      "1    bell  smooth   white\n",
      "2  convex   scaly   white\n",
      "3  convex  smooth    gray\n",
      "4  convex   scaly  yellow\n"
     ]
    }
   ],
   "source": [
    "from yellowbrick.datasets import load_mushroom\r\n",
    "\r\n",
    "X, y = load_mushroom()\r\n",
    "# inspect the first five rows\r\n",
    "# 查看前五行数据\r\n",
    "print(X[:5]) "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 1.3 特征提取\n",
    "我们的数据（包括目标）是分类的。我们将需要将这些值更改为数字值以进行机器学习。为了从数据集中提取数据，我们必须使用scikit-learn转换器将输入数据集转换为适合模型的数据。幸运的是，scikit-learn确实提供了将类别标签转换为数字整数的转换器：[sklearn.preprocessing.LabelEncoder](http://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.LabelEncoder.html)和[sklearn.preprocessing.OneHotEncoder](http://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html)。\n",
    "\n",
    "我们将结合使用scikit-learn的Pipeline对象。\n",
    "```\n",
    "from sklearn.pipeline import Pipeline\n",
    "from sklearn.preprocessing import OneHotEncoder, LabelEncoder\n",
    "\n",
    "# Label-encode targets before modeling\n",
    "# 标记编码目标\n",
    "y = LabelEncoder().fit_transform(y)\n",
    "\n",
    "# One-hot encode columns before modeling\n",
    "# 建立一个热编码建模器\n",
    "model = Pipeline([\n",
    " ('one_hot_encoder', OneHotEncoder()),\n",
    " ('estimator', estimator)\n",
    "])\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "## 1.4 建模与评估\n",
    "### 1.4.1 评估分类器的通用指标\n",
    "\n",
    "精确度是正确阳性结果的数量除以所有阳性结果的数量（例如，我们预测的蘑菇中实际有多少可以食用？）。\n",
    "召回率是正确的阳性结果数除以应该返回的阳性结果数（例如，我们准确预测了多少有毒的蘑菇是有毒的？）。\n",
    "F1成绩是测试准确性的一个衡量标准。它同时考虑了测试的精确性和召回率来计算分数。F1分数可以解释为精确度和召回率的加权平均值，F1分数在1时达到最佳值，最差值在0时达到最差值。\n",
    "\n",
    "其计算公式如下：\n",
    "```\n",
    "precision = true positives / (true positives + false positives)\n",
    "\n",
    "recall = true positives / (false negatives + true positives)\n",
    "\n",
    "F1 score = 2 * ((precision * recall) / (precision + recall))\n",
    "```\n",
    "\n",
    "现在我们准备做出一些预测！\n",
    "\n",
    "让我们建立一种评估多个估计量的方法-首先使用传统的数字评分（稍后将与Yellowbrick库中的某些视觉诊断进行比较）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "SVC: 0.6624286455630514\n",
      "NuSVC: 0.6726016476215785\n",
      "LinearSVC: 0.6583804143126177\n",
      "SGDClassifier: 0.6730980583709855\n",
      "KNeighborsClassifier: 0.6581185045215279\n",
      "LogisticRegression: 0.6580434509606933\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/sklearn/linear_model/logistic.py:757: ConvergenceWarning: lbfgs failed to converge. Increase the number of iterations.\n",
      "  \"of iterations.\", ConvergenceWarning)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "LogisticRegressionCV: 0.6583804143126177\n",
      "BaggingClassifier: 0.6871364804544838\n",
      "ExtraTreesClassifier: 0.6871364804544838\n",
      "RandomForestClassifier: 0.6873056644585642\n"
     ]
    }
   ],
   "source": [
    "from sklearn.metrics import f1_score\r\n",
    "from sklearn.pipeline import Pipeline\r\n",
    "from sklearn.svm import LinearSVC, NuSVC, SVC\r\n",
    "from sklearn.neighbors import KNeighborsClassifier\r\n",
    "from sklearn.preprocessing import OneHotEncoder, LabelEncoder\r\n",
    "from sklearn.linear_model import LogisticRegressionCV, LogisticRegression, SGDClassifier\r\n",
    "from sklearn.ensemble import BaggingClassifier, ExtraTreesClassifier, RandomForestClassifier\r\n",
    "\r\n",
    "\r\n",
    "models = [\r\n",
    "    SVC(gamma='auto'), NuSVC(gamma='auto'), LinearSVC(),\r\n",
    "    SGDClassifier(max_iter=100, tol=1e-3), KNeighborsClassifier(),\r\n",
    "    LogisticRegression(solver='lbfgs'), LogisticRegressionCV(cv=3),\r\n",
    "    BaggingClassifier(), ExtraTreesClassifier(n_estimators=300),\r\n",
    "    RandomForestClassifier(n_estimators=300)\r\n",
    "]\r\n",
    "\r\n",
    "\r\n",
    "def score_model(X, y, estimator, **kwargs):\r\n",
    "    \"\"\"\r\n",
    "    Test various estimators.\r\n",
    "    \"\"\"\r\n",
    "    y = LabelEncoder().fit_transform(y)\r\n",
    "    model = Pipeline([\r\n",
    "        ('one_hot_encoder', OneHotEncoder()),\r\n",
    "        ('estimator', estimator)\r\n",
    "    ])\r\n",
    "\r\n",
    "    # Instantiate the classification model and visualizer\r\n",
    "    # 初始化模型\r\n",
    "    model.fit(X, y, **kwargs)\r\n",
    "\r\n",
    "    # 真实值\r\n",
    "    expected  = y\r\n",
    "    # 预测值\r\n",
    "    predicted = model.predict(X)\r\n",
    "\r\n",
    "    # Compute and return F1 (harmonic mean of precision and recall)\r\n",
    "    #   #计算并返回F1（精度和召回率的平均值）\r\n",
    "    print(\"{}: {}\".format(estimator.__class__.__name__, f1_score(expected, predicted)))\r\n",
    "\r\n",
    "for model in models:\r\n",
    "    score_model(X, y, model);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "初步模型评估看来，根据上述F1分数的结果，哪种模型表现最好？\n",
    "\n",
    "SGDClassifier\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "### 1.4.2 视觉模型评估\n",
    "现在，让我们重构我们的模型评估函数，以使用Yellowbrick的ClassificationReport类，一个显示精度、召回率和F1分数的模型可视化工具。这个可视化的模型分析工具集成了数字分数和彩色编码的热图，以支持简单的解释和检测，特别是I型和II型错误的细微差别，它们与我们的用例非常相关（甚至可以挽救生命）！\n",
    "\n",
    "I型错误（或“假阳性”）是指检测不存在的影响（例如，当蘑菇事实上可以食用时，但判断为蘑菇有毒）。  \n",
    "II错误（或“假阴性”）是指未能检测到存在的影响（例如，蘑菇实际上有毒，但可判断为以食用）。\n",
    "\n",
    "因此我们通过一下代码显示了各个模型的混淆矩阵"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from sklearn.pipeline import Pipeline\r\n",
    "from yellowbrick.classifier import ClassificationReport\r\n",
    "\r\n",
    "\r\n",
    "def visualize_model(X, y, estimator, **kwargs):\r\n",
    "    \"\"\"\r\n",
    "    Test various estimators.\r\n",
    "    \"\"\"\r\n",
    "    y = LabelEncoder().fit_transform(y)\r\n",
    "    model = Pipeline([\r\n",
    "        ('one_hot_encoder', OneHotEncoder()),\r\n",
    "        ('estimator', estimator)\r\n",
    "    ])\r\n",
    "\r\n",
    "    # Instantiate the classification model and visualizer\r\n",
    "    visualizer = ClassificationReport(\r\n",
    "        model, classes=['edible', 'poisonous'],\r\n",
    "        cmap=\"YlGn\", size=(600, 360), **kwargs\r\n",
    "    )\r\n",
    "    visualizer.fit(X, y)\r\n",
    "    visualizer.score(X, y)\r\n",
    "    visualizer.show()\r\n",
    "\r\n",
    "for model in models:\r\n",
    "    visualize_model(X, y, model)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": false
   },
   "source": [
    "# 2 参考\n",
    "[https://www.scikit-yb.org/en/latest/tutorial.html#modeling-and-evaluation](https://www.scikit-yb.org/en/latest/tutorial.html#modeling-and-evaluation)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "PaddlePaddle 1.8.0 (Python 3.5)",
   "language": "python",
   "name": "py35-paddle1.2.0"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
